//============================================================================================
/**
 * @file	shot_ai.c
 * @brief	シュートAI
 * @author	Nozomu Saito
 *
 * モジュール名：ShotAI_
 */
//============================================================================================

#include "shot_ai.h"
#include "pkthln_shot_local.h"

#define POKE_NUM_MAX	(ENTRY_MAX*ENTRY_MEMBER_NUM_MAX)
#define EXE_FRAME_ALONE	(3)			//非通信時AI実行フレーム間隔
#define EXE_FRAME_COMM	(3)			//通信時AI実行フレーム間隔

#define AREA_OVER	(4)

#define FLD_CENTER_X	(128)
#define FLD_CENTER_Y	(96)

#define KEEP_POS_RANGE	(8)
#define GK_POS_NUM		(3)

#define KEEPER_STAMINA	(50)

#define FREE_RUN_WIDTH	(64)
#define FREE_RUN_HEIGHT	(32)

//デバッグ
#ifdef PM_DEBUG
//#define ATTACK_INVALID		//アタッカー無効
//#define DEFENSE_INVALID		//キーパー無効
//#define FREE_INVALID		//フリー無効
//#define ONLY_ONE			//NPC1人
#endif


//#define DEBUG_AI_PRINT

enum {
	KEEPER = 0,
	ATTACKER = 1,
	FREETER = 2,
};

enum {
	AT_ACT_NONE = 0,
	AT_ACT_HIT_CLEAR,
	AT_ACT_CHASE_OTHER,
	AT_ACT_CHASE_SAME,
	AT_ACT_SHOT,

	DF_ACT_NONE,
	DF_ACT_MOVE,
	DF_ACT_CLEAR,

	FR_ACT_NONE,
	FR_ACT_HIT_CLEAR,
	FR_ACT_RUN,
	FR_ACT_MOVE,
	FR_ACT_CLEAR,
	FR_ACT_SHOT,
};

typedef struct SHOT_NPC_POKE_tag{
	int ActWait;
	SHOT_POKE *ShotPoke;
}SHOT_NPC_POKE;

typedef struct SHOT_AI_WORK_tag
{
	SHOT_NPC_POKE NpcPoke[ENTRY_MAX];	//0:キーパー　1：アタッカー　2：フリーター
	SHOT_POKE *EntryPoke[ENTRY_MAX];
}SHOT_AI_WORK;

typedef struct SHOT_AI_CONT_tag
{
	SHOT_AI_WORK	Work[ENTRY_MEMBER_NUM_MAX];

	SHOT_BALL		*Balls;

	BOOL Execute;
	u8 NpcNum;	//0〜3
	u8 ExeNpcIdx;	//0〜2
	u8 Frame;
	u8 CntFrame;
	u8 ExePokeIdx;	//0〜2
	u8 NpcStartIdx;	//1〜3
	u8 PlayerNum;	//1〜4
	u8 dummy;
}SHOT_AI_CONT;

typedef struct GK_POS_tag
{
	u16 x;
	u16 y;
}GK_POS;

static void SetPokePosition(SHOT_AI_WORK *work);

static void ExecuteAI( PKTHLN_PTR work, SHOT_AI_CONT_PTR ptr, SHOT_NPC_POKE *poke );
static u8 GetArea( const int inBaseX, const int inBaseY, const int inX, const int inY );
static const BOOL CheckShotArea(const int inBallX, const int inBallY, const int inX, const int inY,
								const u8 inPokeFldArea, const u8 inMyFldArea);
static const BOOL CheckClearArea(const int inBallX, const int inBallY, const int inX, const int inY,
								const u8 inPokeFldArea, const u8 inMyFldArea);
static BOOL CheckDefenseArea(const int inX, const int inY, const GK_POS *inPosList);

static u8 ExeAttackAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx, SHOT_POKE *poke, SHOT_BALL* balls);
static u8 AttackAct(SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction, const u8 inNowFldArea);
static u8 ExeDefenseAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx, SHOT_POKE *poke, SHOT_BALL* balls);
static u8 DefenseAct(SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction, const u8 inMyFldArea);
static u8 ExeFreeAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx, SHOT_POKE *poke, SHOT_BALL* balls,
						const SHOT_AI_WORK *inAiWork);
static u8 FreeAct(	SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction,
					const u8 inMyFldArea, const SHOT_AI_WORK *inAiWork	);
static u8 FreeRun(SHOT_POKE *free_poke, const SHOT_AI_WORK *inAiWork, const u8 inMyFldArea);

static BOOL CheckHitCircle(const VecFx32 *inVec1, const fx32 inRange1,
		const VecFx32 *inVec2, const fx32 inRange2);


static const GK_POS KeeperPos[ENTRY_MEMBER_NUM_MAX-1][GK_POS_NUM] = {
	{
		{26*8,8*8},{23*8,6*8},{28*8,11*8}
	},
	{
		{6*8,18*8},{9*8,20*8},{4*8,15*8}
	},
	{
		{26*8,18*8},{23*8,20*8},{28*8,15*8}
	},
};

//----------------------------------------------------------------------------
/**
 *	@brief	初期化
 *
 *	@param	inHeapID		ヒープID
 *	@param	inNpcNum		NPCの数
 *	@param	inType			ゲームタイプ（通信してるか）
 *
 *	@return ptr				コントロールポインタ
 */
//-----------------------------------------------------------------------------
SHOT_AI_CONT_PTR ShotAI_Init(const int inHeapID, const int inNpcNum, const PKTHLN_GAME_TYPE inType)
{
	SHOT_AI_CONT_PTR cnt_ptr;
	//メモリアロケーション
	int size = sizeof(SHOT_AI_CONT);
	cnt_ptr = sys_AllocMemory(inHeapID, size);
	MI_CpuClear8( cnt_ptr, size);

	cnt_ptr->NpcNum = inNpcNum;
	cnt_ptr->PlayerNum = ENTRY_MEMBER_NUM_MAX-inNpcNum;
	cnt_ptr->NpcStartIdx = cnt_ptr->PlayerNum;
	if (inType == PKTHLN_GAME_ALONE){
		cnt_ptr->CntFrame = EXE_FRAME_ALONE;
	}else{
		cnt_ptr->CntFrame = EXE_FRAME_COMM;
	}
	cnt_ptr->Frame = cnt_ptr->CntFrame;	//すぐに起動するようにしておく
	return cnt_ptr;
}

//----------------------------------------------------------------------------
/**
 *	@brief	AIセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inPokeIdx		ポケモンインデックス
 *	@param	poke			競技ポケモン
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ShotAI_SetupPoke(SHOT_AI_CONT_PTR ptr , const u8 inPokeIdx, SHOT_POKE *poke)
{
	u8 idx;
	u8 net_id = inPokeIdx / ENTRY_MAX;

	idx = inPokeIdx % 3;
///	ptr->Work[ net_id ].NpcPoke[idx].ShotPoke = poke;
	ptr->Work[ net_id ].EntryPoke[idx] = poke;
}

//----------------------------------------------------------------------------
/**
 *	@brief	役割決定
 *
 *	@param	ptr				コントロールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ShotAI_SetPokePosition(SHOT_AI_CONT_PTR ptr)
{
	u8 i;
	for (i=0;i<ENTRY_MEMBER_NUM_MAX;i++){
		SetPokePosition(&ptr->Work[i]);
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	役割決定
 *
 *	@param	work			ワークポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void SetPokePosition(SHOT_AI_WORK *work)
{
	u8 i,j;
	u8 tq_st[ENTRY_MAX];
	u8 tq_pw[ENTRY_MAX];
	u8 keeper, attacker ,freeter;

	//選定に使用するパラメータをセット
	for (i=0;i<ENTRY_MAX;i++){
		SHOT_POKE *poke;
		poke = work->EntryPoke[i];
		tq_st[i] = poke->Param.Technique+poke->Param.StaminaMax;
		tq_pw[i] = poke->Param.Technique+poke->Param.Power;
	}

	//キーパー確定
	keeper = 0;		//キーパー候補
	for (i=1;i<ENTRY_MAX;i++){
		if ( tq_st[i-1] <tq_st[i] ){
			keeper = i;
		}
	}

	if ( keeper == 0 ){		//0番目がキーパーなら
		//1番2番でアタッカー・フリーターを決める
		if ( tq_pw[1] >= tq_pw[2] ){
			attacker = 1;
			freeter = 2;
		}else{
			attacker = 2;
			freeter = 1;
		}
	}else if( keeper == 1 ){
		//0番2番でアタッカー・フリーターを決める
		if ( tq_pw[0] >= tq_pw[2] ){
			attacker = 0;
			freeter = 2;
		}else{
			attacker = 2;
			freeter = 0;
		}
	}else{	//keeper == 2
		//0番1番でアタッカー・フリーターを決める
		if ( tq_pw[0] >= tq_pw[1] ){
			attacker = 0;
			freeter = 1;
		}else{
			attacker = 1;
			freeter = 0;
		}
	}
	//役割セット
	work->NpcPoke[0].ShotPoke = work->EntryPoke[keeper];
	work->NpcPoke[1].ShotPoke = work->EntryPoke[attacker];
	work->NpcPoke[2].ShotPoke = work->EntryPoke[freeter];
}

//----------------------------------------------------------------------------
/**
 *	@brief	AI用ボールポインタセット
 *
 *	@param	ptr				コントロールポインタ
 *	@param	balls			ボールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ShotAI_SetupBall(SHOT_AI_CONT_PTR ptr , SHOT_BALL *balls )
{
	ptr->Balls = balls;
}

//----------------------------------------------------------------------------
/**
 *	@brief	解放
 *
 *	@param	ptr				コントロールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ShotAI_End(SHOT_AI_CONT_PTR ptr)
{
	//メモリ解放
	sys_FreeMemoryEz(ptr);
}

//----------------------------------------------------------------------------
/**
 *	@brief	ＡＩ実行
 *
 *  @param	work		ポケスロンワークポインタ
 *	@param	ptr			管理ポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void ShotAI_Execute(PKTHLN_PTR work, SHOT_AI_CONT_PTR ptr)
{
	if (ptr->PlayerNum == ENTRY_MEMBER_NUM_MAX){
		//AIなし
		return;
	}

	ptr->Frame++;

	if (ptr->Frame >= ptr->CntFrame){
		u8 i;
		//実行状態にする
		ptr->Execute = TRUE;
		ptr->Frame = 0;
	}

	if (ptr->Execute){	//実行状態
		SHOT_AI_WORK *ai_work;
		SHOT_NPC_POKE *poke;
		ai_work = &ptr->Work[ptr->NpcStartIdx+ptr->ExeNpcIdx];
		poke = &ai_work->NpcPoke[ ptr->ExePokeIdx ];

#ifdef ONLY_ONE
		//デバッグ処理
		if ( ptr->ExeNpcIdx != 0 ){
			//実行NPCのシフト
			ptr->ExeNpcIdx++;
			if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
				ptr->ExeNpcIdx = 0;
				//実行するポケモンインデックスを変更する
				ptr->ExePokeIdx = (ptr->ExePokeIdx+1) % ENTRY_MAX;
				//実行フラグを落とす
				ptr->Execute = FALSE;
			}
			return;
		}
#endif
		//実行NPCのポケ決定
		if ( poke->ActWait == 0 ){		//アクションウェイト調査
			//実行可能
			ExecuteAI(work, ptr, poke);
		}else{		//アクションウェイト減算
			ai_work->NpcPoke[ ptr->ExePokeIdx ].ActWait--;
		}

		//実行NPCのシフト
		ptr->ExeNpcIdx++;

		if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
			ptr->ExeNpcIdx = 0;
			//実行するポケモンインデックスを変更する
			ptr->ExePokeIdx = (ptr->ExePokeIdx+1) % ENTRY_MAX;
			//実行フラグを落とす
			ptr->Execute = FALSE;
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行	アタック
 *
 *  @param	work			ポケスロンワークポインタ
 *  @param	inMyFldArea		自分のフィールドエリア
 *  @param	inPokeIdx		ポケモンインデックス
 *  @param	poke			競技ポケモン
 *	@param	balls			ボールポインタ
 *
 *	@return	u8				アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 ExeAttackAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx,
							SHOT_POKE *poke, SHOT_BALL* balls)
{
	u8 act_wait;
	u8 i;
	u8 poke_fld_area;
	u8 ball_action[BALL_MAX];

	//疲労・気絶・ダッシュ中なら処理終了
	if ( poke->State != SP_STATE_NORMAL ){
		return 0;
	}
	//ノックバック中なら処理終了
	if (poke->KnockBack.Type != KNOCKBACK_NONE){
		return 0;
	}

	//自分のフィールドエリアを取得
	poke_fld_area = GetArea( FLD_CENTER_X, FLD_CENTER_Y, poke->NowPoint.x / FX32_ONE, poke->NowPoint.y / FX32_ONE );

	for (i=0;i<BALL_MAX;i++){
		u8 ball_fld_area;
		SHOT_BALL *ball;
		ball = &balls[i];
		//ボールのエリアを取得　蹴れない状態ならエリア所属無しとする
		if ( (ball->State == BALL_STATE_NORMAL)&&((ball->Type != BALL_MV_TYPE_NONE)) ){
			ball_fld_area = GetArea( FLD_CENTER_X, FLD_CENTER_Y, ball->Pos.x / FX32_ONE, ball->Pos.y / FX32_ONE );
		}else{
			ball_fld_area = AREA_OVER;
		}
		if ( ball_fld_area != AREA_OVER){
			int ball_r = 0;
			//ボールとの当たり判定を確認重なっていたら、当たり判定の回復を図る
			if (ball->Type == BALL_TYPE_NORMAL){
				ball_r = NORM_BALL_RANGE;
			}else if(ball->Type == BALL_TYPE_GOLD){
				ball_r = GOLD_BALL_RANGE;
			}else{
				GF_ASSERT(0);
			}
			if ( CheckHitCircle(&poke->NowPoint, PokeAct_GetHitRange(poke->PokeActPtr)*FX32_ONE,
								&ball->Pos, ball_r*FX32_ONE) ){
				//当たり判定の回復を図る
				ball_action[i] = AT_ACT_HIT_CLEAR;
			}else{
				//ボールと同じフィールドエリアかを判定
				if ( poke_fld_area == ball_fld_area ){		//同じフィールドエリア
					//シュートエリアチェック
					if ( CheckShotArea( ball->Pos.x/FX32_ONE, ball->Pos.y/FX32_ONE,
										poke->NowPoint.x/FX32_ONE, poke->NowPoint.y/FX32_ONE,
										poke_fld_area, inMyFldArea) ){		//シュートエリア
						//シュート処理
						ball_action[i] = AT_ACT_SHOT;		//シュート
					}else{
						//同じフィールドエリアにあるボールを追いかける
						ball_action[i] = AT_ACT_CHASE_SAME;		//同じエリアのボールを追いかける
					}
				}else{
					ball_action[i] = AT_ACT_CHASE_OTHER;		//別のエリアボールを追いかける
				}
			}
		}else{
			ball_action[i] = AT_ACT_NONE;		//何もできない
		}
	}

	//行動を決定（評価の高い行動を行う）
	if ( ball_action[0] >= ball_action[1]){
		//インデックス0番のボールに対して行動する
		act_wait = AttackAct(poke, &balls[0], ball_action[0], poke_fld_area);
	}else{
		//インデックス1番のボールに対して行動する
		act_wait = AttackAct(poke, &balls[1], ball_action[1], poke_fld_area);
	}

	if (poke->State == SP_STATE_DASH){
		PTFrame_AddActionPoint(	work, inMyFldArea, inPokeIdx, ADD_TYPE_DASH, 1 );
	}

	return act_wait;
}

//----------------------------------------------------------------------------
/**
 *	@brief	アタッカーアクション
 *
 *	@param	poke				競技ポケモン
 *	@param	ball				ボールポインタ
 *	@param	inAction			アクションコード
 *	@param	inNowFldArea		フィールドエリア
 *
 *	@return	u8		アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 AttackAct(SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction, const u8 inNowFldArea)
{
	u8 prob;
	prob = gf_rand()%100;

	switch(inAction){
	case AT_ACT_NONE:
	case FR_ACT_NONE:
		//何もしない
		break;
	case AT_ACT_CHASE_OTHER:
		//追いかける
		if (prob < 60){		//60% 何もしない
			return 0;
		}else if(prob<80){  //20%　歩行
			poke->LastPoint = ball->Pos;
			return 10;
		}else{		//20%　ダッシュ
			poke->LastPoint = ball->Pos;
			poke->State = SP_STATE_DASH;
			return 10;
		}
	case AT_ACT_CHASE_SAME:
		//追いかける
		if (prob < 20){		//20% 何もしない
			return 0;
		}else if(prob<90){  //70%　歩行
			poke->LastPoint = ball->Pos;
			return 3;
		}else{		//10%　ダッシュ
			poke->LastPoint = ball->Pos;
			poke->State = SP_STATE_DASH;
			return 3;
		}
	case AT_ACT_SHOT:
	case FR_ACT_SHOT:
		//シュートする
		poke->LastPoint = ball->Pos;
		poke->State = SP_STATE_DASH;
		break;
	case AT_ACT_HIT_CLEAR:
	case FR_ACT_HIT_CLEAR:
		//現在いるフィールドエリアの対面のエリアのゴールキーパーポイントを目指す
		{
			u8 area;
			if (inNowFldArea != AREA_OVER){
				area = 3-inNowFldArea;
				poke->LastPoint.x = KeeperPos[area][0].x * FX32_ONE;
				poke->LastPoint.y = KeeperPos[area][0].y * FX32_ONE;
			}else{
				GF_ASSERT(0);
			}
		}
		break;
	default:
		GF_ASSERT(0);
	}
	return 0;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行	ディフェンス
 *
 *  @param	work				ポケスロンワークポインタ
 *  @param	inMyFldArea			自分のフィールドエリア
 *	@param	inPokeIdx			ポケモンインデックス
 *	@param	poke				競技ポケモン
 *	@param	balls				ボールポインタ
 *
 *	@return	u8				アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 ExeDefenseAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx,
							SHOT_POKE *poke, SHOT_BALL* balls)
{
	u8 act_wait;
	u8 i;
	u8 poke_fld_area;
	u8 ball_action[BALL_MAX];
	//疲労・気絶・ダッシュ中なら処理終了
	if ( poke->State != SP_STATE_NORMAL ){
		return 0;
	}
	//ノックバック中なら処理終了
	if (poke->KnockBack.Type != KNOCKBACK_NONE){
		return 0;
	}

	//自分のフィールドエリアを取得
	poke_fld_area = GetArea( FLD_CENTER_X, FLD_CENTER_Y, poke->NowPoint.x / FX32_ONE, poke->NowPoint.y / FX32_ONE );

	for(i=0;i<BALL_MAX;i++){
		u8 ball_fld_area;
		SHOT_BALL *ball;
		ball = &balls[i];
		//ボールのエリアを取得　蹴れない状態ならエリア所属無しとする
		if ( (ball->State == BALL_STATE_NORMAL)&&((ball->Type != BALL_MV_TYPE_NONE)) ){
			ball_fld_area = GetArea( FLD_CENTER_X, FLD_CENTER_Y, ball->Pos.x / FX32_ONE, ball->Pos.y / FX32_ONE );
		}else{
			ball_fld_area = AREA_OVER;
		}

		if ( ball_fld_area != AREA_OVER){
			//自陣にボールがあるか？
			if ( inMyFldArea == ball_fld_area ){		//ある
				//クリアできるか？
				if ( CheckClearArea( ball->Pos.x/FX32_ONE, ball->Pos.y/FX32_ONE,
									poke->NowPoint.x/FX32_ONE, poke->NowPoint.y/FX32_ONE,
									poke_fld_area, inMyFldArea) ){	//できる
					//ボールクリア
					ball_action[i] = DF_ACT_CLEAR;
				}else{	//できない
					//守備座標へ移動
					ball_action[i] = DF_ACT_MOVE;
				}
			}else{		//ない
				//守備座標へ移動
				ball_action[i] = DF_ACT_MOVE;
			}
		}else{
			ball_action[i] = DF_ACT_NONE;		//何もできない
		}
	}	//end for

	//行動を決定（評価の高い行動を行う）
	if ( (ball_action[0]==DF_ACT_CLEAR)==(ball_action[1]==DF_ACT_CLEAR) ){
		fx32 r1, r2;
		VecFx32 vec;
		//ともにクリア条件を満たした場合、距離の短い方に反応
		VEC_Subtract( &poke->NowPoint, &balls[0].Pos, &vec );
		r1 = VEC_Mag(&vec);
		VEC_Subtract( &poke->NowPoint, &balls[1].Pos, &vec );
		r2 = VEC_Mag(&vec);
		if (r1>=r2){
			//インデックス0番のボールに対して行動する
			act_wait = DefenseAct(poke, &balls[0], ball_action[0], inMyFldArea);
		}else{
			//インデックス1番のボールに対して行動する
			act_wait = DefenseAct(poke, &balls[1], ball_action[1], inMyFldArea);
		}
	}else{
		if ( ball_action[0] >= ball_action[1] ){
			//インデックス0番のボールに対して行動する
			act_wait = DefenseAct(poke, &balls[0], ball_action[0], inMyFldArea);
		}else{
			//インデックス1番のボールに対して行動する
			act_wait = DefenseAct(poke, &balls[1], ball_action[1], inMyFldArea);
		}
	}

	//ダッシュした？
	if (poke->State == SP_STATE_DASH){
		PTFrame_AddActionPoint(	work, inMyFldArea, inPokeIdx, ADD_TYPE_DASH, 1 );
	}

	return act_wait;
}

//----------------------------------------------------------------------------
/**
 *	@brief	ディフェンスアクション
 *
 *	@param	poke				競技ポケモン
 *	@param	ball				ボールポインタ
 *	@param	inAction			アクションコード
 *	@param	inMyFldArea			自分のフィールドエリア
 *
 *	@return		u8				アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 DefenseAct(SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction, const u8 inMyFldArea)
{
	switch(inAction){
	case DF_ACT_NONE:
	case FR_ACT_NONE:
		//何もしない
		break;
	case DF_ACT_MOVE:
	case FR_ACT_MOVE:
		{
			GK_POS pos;

			BOOL gkp = CheckDefenseArea( poke->NowPoint.x/FX32_ONE, poke->NowPoint.y/FX32_ONE,
											KeeperPos[inMyFldArea-1]);
			//既に守備座標にいる？
			if ( gkp ){			//既にいる
				return 0;
			}

			//守備位置算出
			if ( (gf_rand()%2)==0 ){	//真ん中
				pos = KeeperPos[inMyFldArea-1][0];
			}else{
				if ( (gf_rand()%2)==0 ){	//左
					pos = KeeperPos[inMyFldArea-1][1];
				}else{						//右
					pos = KeeperPos[inMyFldArea-1][2];
				}
			}

			//触れ幅を持たせる
			pos.x += ( KEEP_POS_RANGE - ( gf_rand() % (KEEP_POS_RANGE*2+1) ) );
			pos.y += ( KEEP_POS_RANGE - ( gf_rand() % (KEEP_POS_RANGE*2+1) ) );
			//守備位置へ
			poke->LastPoint.x = pos.x * FX32_ONE;
			poke->LastPoint.y = pos.y * FX32_ONE;
		}
		return 6;
	case DF_ACT_CLEAR:
	case FR_ACT_CLEAR:
		//ボールクリア
		poke->LastPoint = ball->Pos;
		if ( (gf_rand()%100)<80){	//8割りでダッシュ
			poke->State = SP_STATE_DASH;
		}
		return 10;
	default:
		GF_ASSERT(0);
	}
	return 0;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行	フリー
 *
 *	@param	work				ポケスロンワークポインタ
 *  @param	inMyFldArea			自分のフィールドエリア
 *	@param	inPokeIdx			ポケモンインデックス
 *	@param	poke				競技ポケモン
 *	@param	balls				ボールポインタ
 *	@param	inAiWork			AIワーク
 *
 *	@return	u8				アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 ExeFreeAction(PKTHLN_PTR work, const u8 inMyFldArea, const u8 inPokeIdx,
						SHOT_POKE *poke, SHOT_BALL* balls,
						const SHOT_AI_WORK *inAiWork)
{
	u8 act_wait;
	u8 i;
	u8 poke_fld_area;
	u8 ball_action[BALL_MAX];
	u8 ball_fld_area[BALL_MAX];

	const SHOT_POKE *keeper;

	//疲労・気絶・ダッシュ中なら処理終了
	if ( poke->State != SP_STATE_NORMAL ){
		return 0;
	}
	//ノックバック中なら処理終了
	if (poke->KnockBack.Type != KNOCKBACK_NONE){
		return 0;
	}

	//自分のフィールドエリアを取得
	poke_fld_area = GetArea( FLD_CENTER_X, FLD_CENTER_Y, poke->NowPoint.x / FX32_ONE, poke->NowPoint.y / FX32_ONE );

	for (i=0;i<BALL_MAX;i++){
		SHOT_BALL *ball;
		ball = &balls[i];
		//ボールのエリアを取得　蹴れない状態ならエリア所属無しとする
		if ( (ball->State == BALL_STATE_NORMAL)&&((ball->Type != BALL_MV_TYPE_NONE)) ){
			ball_fld_area[i] = GetArea( FLD_CENTER_X, FLD_CENTER_Y, ball->Pos.x / FX32_ONE, ball->Pos.y / FX32_ONE );
		}else{
			ball_fld_area[i] = AREA_OVER;
		}
	}

	for (i=0;i<BALL_MAX;i++){
		SHOT_BALL *ball;
		ball = &balls[i];

		ball_action[i] = FR_ACT_NONE;
		if ( ball_fld_area[i] != AREA_OVER){
			int ball_r = 0;
			//ボールとの当たり判定を確認重なっていたら、シュートできないので、別行動になる
			if (ball->Type == BALL_TYPE_NORMAL){
				ball_r = NORM_BALL_RANGE;
			}else if(ball->Type == BALL_TYPE_GOLD){
				ball_r = GOLD_BALL_RANGE;
			}else{
				GF_ASSERT(0);
			}
			if ( CheckHitCircle(&poke->NowPoint, PokeAct_GetHitRange(poke->PokeActPtr)*FX32_ONE,
								&ball->Pos, ball_r*FX32_ONE) ){
				//シュートはしない
				ball_action[i] = FR_ACT_NONE;
			}else{
				//ボールと同じフィールドエリアかを判定
				if ( poke_fld_area == ball_fld_area[i] ){		//同じフィールドエリア
					//シュートエリアチェック
					if ( CheckShotArea( ball->Pos.x/FX32_ONE, ball->Pos.y/FX32_ONE,
										poke->NowPoint.x/FX32_ONE, poke->NowPoint.y/FX32_ONE,
										poke_fld_area, inMyFldArea) ){		//シュートエリア
						//シュート処理
						ball_action[i] = FR_ACT_SHOT;		//シュート
					}
				}
			}
		}
	}
	//シュート行動をチェック
	if ( ball_action[0] == FR_ACT_SHOT ){
		//インデックス0番のボールに対して行動する
		act_wait = AttackAct(poke, &balls[0], ball_action[0], poke_fld_area);
		return act_wait;
	}else if ( ball_action[1] == FR_ACT_SHOT ){
		//インデックス1番のボールに対して行動する
		act_wait = AttackAct(poke, &balls[1], ball_action[1], poke_fld_area);
		return act_wait;
	}

	keeper = inAiWork[inMyFldArea].NpcPoke[0].ShotPoke;

	//シュートできなかった場合、キーパーのスタミナチェック
	if (keeper->Param.StaminaNow >= KEEPER_STAMINA){		//キーパー元気
		//フリーラン行動
		act_wait = FreeRun(poke, inAiWork, inMyFldArea);
		return act_wait;
	}else{		//キーパースタミナない
		for(i=0;i<BALL_MAX;i++){
			SHOT_BALL *ball;
			ball = &balls[i];

			if ( ball_fld_area[i] != AREA_OVER){
				//自陣にボールがあるか？
				if ( inMyFldArea == ball_fld_area[i] ){		//ある
					//クリアできるか？
					if ( CheckShotArea( ball->Pos.x/FX32_ONE, ball->Pos.y/FX32_ONE,
										poke->NowPoint.x/FX32_ONE, poke->NowPoint.y/FX32_ONE,
										poke_fld_area, inMyFldArea) ){	//できる
						//ボールクリア
						ball_action[i] = FR_ACT_CLEAR;
					}else{	//できない
						//守備座標へ移動
						ball_action[i] = FR_ACT_MOVE;
					}
				}else{		//ない
					//守備座標へ移動
					ball_action[i] = FR_ACT_MOVE;
				}
			}else{
				ball_action[i] = FR_ACT_RUN;		//フリー移動
			}
		}	//end for
	}

	//行動を決定（評価の高い行動を行う）
	if ( (ball_action[0]==DF_ACT_CLEAR)==(ball_action[1]==DF_ACT_CLEAR) ){
		fx32 r1, r2;
		VecFx32 vec;
		//ともにクリア条件を満たした場合、距離の短い方に反応
		VEC_Subtract( &poke->NowPoint, &balls[0].Pos, &vec );
		r1 = VEC_Mag(&vec);
		VEC_Subtract( &poke->NowPoint, &balls[1].Pos, &vec );
		r2 = VEC_Mag(&vec);
		if (r1>=r2){
			//インデックス0番のボールに対して行動する
			act_wait = FreeAct(poke, &balls[0], ball_action[0], inMyFldArea, inAiWork);
		}else{
			//インデックス1番のボールに対して行動する
			act_wait = FreeAct(poke, &balls[1], ball_action[1], inMyFldArea, inAiWork);
		}
	}else{
		if ( ball_action[0] >= ball_action[1] ){
			//インデックス0番のボールに対して行動する
			act_wait = FreeAct(poke, &balls[0], ball_action[0], inMyFldArea, inAiWork);
		}else{
			//インデックス1番のボールに対して行動する
			act_wait = FreeAct(poke, &balls[1], ball_action[1], inMyFldArea, inAiWork);
		}
	}

	//ダッシュした？
	if (poke->State == SP_STATE_DASH){
		PTFrame_AddActionPoint(	work, inMyFldArea, inPokeIdx, ADD_TYPE_DASH, 1 );
	}

	return act_wait;
}

//----------------------------------------------------------------------------
/**
 *	@brief	フリーアクション
 *
 *	@param	poke				競技ポケモン
 *	@param	ball				ボールポインタ
 *	@param	inAction			アクションコード
 *	@param	inMyFldArea			自分のフィールドエリア
 *	@param	inAiWork			AIワーク
 *
 *	@return	u8					アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 FreeAct(	SHOT_POKE *poke, SHOT_BALL *ball, const u8 inAction,
					const u8 inMyFldArea, const SHOT_AI_WORK *inAiWork	)
{
	u8 act_wait;
	if (inAction == FR_ACT_RUN){
		act_wait =  FreeRun(poke, inAiWork, inMyFldArea);
	}else{
		act_wait = DefenseAct(poke, ball, inAction, inMyFldArea);
	}
	return act_wait;
}

//----------------------------------------------------------------------------
/**
 *	@brief	フリーラン動作
 *
 *	@param	free_poke			競技ポケモン
 *  @param	inAiWork			AIワーク
 *	@param	inMyFldArea			自分のフィールドエリア
 *
 *	@return	u8					アクションウェイト
 */
//-----------------------------------------------------------------------------
static u8 FreeRun(SHOT_POKE *free_poke, const SHOT_AI_WORK *inAiWork, const u8 inMyFldArea)
{
	u8 i,j;
	u8 area_num[4] = {0,0,0,0};
	u8 area;
	int ofs_x, ofs_y;
	//密度を調べる
	for (i=0;i<ENTRY_MEMBER_NUM_MAX;i++){
		for(j=0;j<ENTRY_MAX;j++){
			u8 area_idx;
			SHOT_POKE *poke;
			poke = inAiWork[i].NpcPoke[j].ShotPoke;
			area_idx = GetArea( FLD_CENTER_X, FLD_CENTER_Y, poke->NowPoint.x / FX32_ONE, poke->NowPoint.y / FX32_ONE );
			if (area_idx != AREA_OVER){
				area_num[area_idx]++;
			}
		}
	}
	//一番密度の小さいエリアを算出(自エリアを除く)
	area = 0;
	for (i=1;i<4;i++){
		if ( (area_num[i-1] > area_num[i])&&(i!=inMyFldArea) ){
			area = i;
		}
	}
	//座標算出
	ofs_x = gf_rand()%FREE_RUN_WIDTH;
	ofs_y = gf_rand()%FREE_RUN_HEIGHT;
	switch(area){
	case 0:
		free_poke->LastPoint.x = (FLD_CENTER_X-ofs_x) * FX32_ONE;
		free_poke->LastPoint.y = (FLD_CENTER_Y-ofs_y) * FX32_ONE;
		break;
	case 1:
		free_poke->LastPoint.x = (FLD_CENTER_X+ofs_x) * FX32_ONE;
		free_poke->LastPoint.y = (FLD_CENTER_Y-ofs_y) * FX32_ONE;
		break;
	case 2:
		free_poke->LastPoint.x = (FLD_CENTER_X-ofs_x) * FX32_ONE;
		free_poke->LastPoint.y = (FLD_CENTER_Y+ofs_y) * FX32_ONE;
		break;
	case 3:
		free_poke->LastPoint.x = (FLD_CENTER_X+ofs_x) * FX32_ONE;
		free_poke->LastPoint.y = (FLD_CENTER_Y+ofs_y) * FX32_ONE;
		break;
	default:
		GF_ASSERT(0);
	}
	return 10;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行
 *
 *  @param	work			ポケスロンワーク
 *	@param	ptr				AIコントローラポインタ
 *	@param	poke			NPCポケモン
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void ExecuteAI( PKTHLN_PTR work, SHOT_AI_CONT_PTR ptr, SHOT_NPC_POKE *poke )
{
	u8 my_fld_area;
	u8 poke_idx;

	my_fld_area = ptr->NpcStartIdx + ptr->ExeNpcIdx;
	poke_idx = ptr->ExePokeIdx;

	switch(ptr->ExePokeIdx){
	case KEEPER:
#ifndef ATTACK_INVALID
		poke->ActWait = ExeDefenseAction(work, my_fld_area, poke_idx,
											poke->ShotPoke, ptr->Balls);
#endif
		break;
	case ATTACKER:
#ifndef DEFENSE_INVALID
		poke->ActWait = ExeAttackAction(work, my_fld_area, poke_idx,
											poke->ShotPoke, ptr->Balls);
#endif
		break;
	case FREETER:
#ifndef FREE_INVALID
		poke->ActWait = ExeFreeAction(work,	my_fld_area, poke_idx,
											poke->ShotPoke, ptr->Balls, ptr->Work);
#endif
		break;
	default:
		GF_ASSERT(0);
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	エリア取得関数
 *
 *	@param	inBaseX		象限分割中心X座標
 *	@param	inBaseY		象限分割中心X座標
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *
 *	@return	u8			エリアインデックス（0〜3）
 */
//-----------------------------------------------------------------------------
static u8 GetArea( const int inBaseX, const int inBaseY, const int inX, const int inY )
{
	u8 idx = 0;

	if ( ( inX < inBaseX ) && ( inY < inBaseY ) ){
		//左上
		idx = 0;
	}else if( ( inX >= inBaseX ) && ( inY < inBaseY ) ){
		//右上
		idx = 1;
	}else if( ( inX < inBaseX ) && ( inY >= inBaseY ) ){
		//左下
		idx = 2;
	}else if( ( inX >= inBaseX ) && ( inY >= inBaseY ) ){
		//右下
		idx = 3;
	}else{
		GF_ASSERT(0);
		idx = AREA_OVER;
	}

	return idx;
}

//----------------------------------------------------------------------------
/**
 *	@brief	シュートエリア判定関数
 *
 *	@param	inBallX		ボールX座標
 *	@param	inBallY		ボールX座標
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *	@param	inPokeFldArea	判定するフィールドエリア
 *	@param	inMyFldArea	自チームのエリア	（NPCなので1〜3）
 *
 *	@return	BOOL		TRUE:エリア内	FALSE:エリア外
 */
//-----------------------------------------------------------------------------
static const BOOL CheckShotArea(const int inBallX, const int inBallY, const int inX, const int inY,
								const u8 inPokeFldArea, const u8 inMyFldArea)
{
	u8 shot_area;
	u8 poke_area;
	poke_area = GetArea( inBallX, inBallY, inX,inY );
	//自チームフィールドエリアと同じフィールドエリアに自分がいる
	if ( inMyFldArea == inPokeFldArea ){
		shot_area = inMyFldArea;
	}else{
		shot_area = 3 - inPokeFldArea;
	}

	if (poke_area == shot_area){
			return TRUE;
	}

	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	クリアエリア判定関数
 *
 *	@param	inBallX		ボールX座標
 *	@param	inBallY		ボールX座標
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *	@param	inPokeFldArea	判定するフィールドエリア
 *	@param	inMyFldArea	自チームのエリア	（NPCなので1〜3）
 *
 *	@return	BOOL	TRUE:クリアエリア	FALSE:クリアエリア以外
 */
//-----------------------------------------------------------------------------
static const BOOL CheckClearArea(const int inBallX, const int inBallY, const int inX, const int inY,
								const u8 inPokeFldArea, const u8 inMyFldArea)
{
	u8 poke_area;
	poke_area = GetArea( inBallX, inBallY, inX,inY );

	//自チームフィールドエリアと同じフィールドエリアに自分がいる
	if ( inMyFldArea == inPokeFldArea ){
		if ( 3-inMyFldArea == poke_area ){
			return FALSE;
		}
		return TRUE;
	}
	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	守備座標かをチェック
 *
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *	@param	inPosList	守備座標リスト
 *
 *	@return	BOOL		TRUE:守備座標にいる		FALSE:守備座標にいない
 */
//-----------------------------------------------------------------------------
static BOOL CheckDefenseArea(const int inX, const int inY, const GK_POS *inPosList)
{
	u8 i;
	for (i=0;i<GK_POS_NUM;i++){
		int dif_x, dif_y;
///		OS_Printf( "dfpoint =%d,%d\n",inPosList[i].x,inPosList[i].y );
		dif_x = inX - inPosList[i].x;
		dif_y = inY - inPosList[i].y;
		if ( ( (-KEEP_POS_RANGE <= dif_x)&&(dif_x <=KEEP_POS_RANGE) )&&
			 ( (-KEEP_POS_RANGE <= dif_y)&&(dif_y <=KEEP_POS_RANGE) ) ){
			return TRUE;
		}
	}
	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	円同士の当たり判定(接触含まず)
 *
 *	@param	inVec1			中心座標
 *	@param	inRange1		半径
 *	@param	inVec2			中心座標
 *	@param	inRange2		半径
 *
 *	@return	BOOL		TRUE:ヒット		FALSE:非ヒット
 */
//-----------------------------------------------------------------------------
static BOOL CheckHitCircle(const VecFx32 *inVec1, const fx32 inRange1,
		const VecFx32 *inVec2, const fx32 inRange2)
{
	fx32 len;
	VecFx32 vec;
	VEC_Subtract( inVec1, inVec2, &vec );

	//方向ベクトルの長さを取得
	len = VEC_Mag(&vec);

	if (len < inRange1+inRange2){
		return TRUE;
	}

	return FALSE;
}


